#include <QCoreApplication>
#include <QJsonDocument>
#include <QVariantMap>
#include <QFile>
#include <QDir>

QString inputRoot;
QString outputDir = "./";

QMap<QString,QString> settings;

QMap<QString,QString> marks;

QString readOne(const QStringList& args,int& counter){
    if(counter == args.size())
    {
        throw QString("quit");
    }
    auto ret = args[counter];
    counter ++;
    return ret;
}

void parseArgs(const QStringList& args){

    int counter = 0;

    try{
        while (true) {
            QString cmd = readOne(args,counter);
            if(cmd == "-o")
            {
                outputDir = readOne(args,counter);
            }

            if(cmd == "-p")
            {
                auto key = readOne(args,counter);
                auto value = readOne(args,counter);
                settings[key] = value;
            }
        }
    }
    catch(const QString& s){
        return;
    }
}

struct Project
{
    QString rootDir;
    QStringList dirs;
    QStringList files;

};

void foreachFile(QFileInfo root,const std::function<void(const QFileInfo& info)>& call){
    call(root);

    if(root.isDir())
    {
        for(auto i:QDir(root.absoluteFilePath()).entryInfoList(QDir::Filter::NoDotAndDotDot | QDir::Files | QDir::Dirs))
        {
            foreachFile(i,call);
        }
    }
}

Project readProject(QString root){

    Project ret;
    auto rootInfo = QFileInfo(root);
    ret.rootDir = rootInfo.absolutePath();
    foreachFile(rootInfo,[&](const QFileInfo& info){

        QString filePath = info.absoluteFilePath();

        filePath.remove(0,ret.rootDir.size());

        if(info.isFile())
        {
            ret.files << filePath;
        }
        else if(info.isDir())
        {
            ret.dirs << filePath;
        }

    });
    return ret;
}

QString translateString(QString input,const QMap<QString,QString> &markMap){
    for(auto mark:markMap.keys())
    {
        auto value = markMap[mark];

        input.replace(mark,value);
    }

    return input;
}

QByteArray translateBytes(QByteArray input,const QMap<QString,QString> &markMap){
    for(auto mark:markMap.keys())
    {
        auto value = markMap[mark];

        input.replace(mark.toLocal8Bit(),value.toLocal8Bit());
    }

    return input;
}

QMap<QString,QString> getMarkMap(){
    QMap<QString,QString> ret;
    for(auto markName:settings.keys())
    {
        if(!marks.contains(markName))
        {
            continue;
        }
        auto value = settings[markName];
        auto mark = marks[markName];
        ret[mark] = value;
    }

    return ret;
}

void createProject(const Project & pro,QString output,const QMap<QString,QString> &markMap){
    for(auto i:pro.dirs)
    {
        QString path = translateString(i,markMap);
        QString dst = output + path;
        QDir().mkpath(dst);
    }

    for(auto i:pro.files)
    {
        QString path = translateString(i,markMap);
        QString targetPath = output + path;
        QString srcPath = pro.rootDir + i;
        QFile src(srcPath);
        src.open(QIODevice::ReadOnly);
        auto content = src.readAll();
        content = translateBytes(content,markMap);

        QFile dst(targetPath);
        dst.open(QIODevice::WriteOnly | QIODevice::Truncate);
        dst.write(content);
    }
}

int main(int argc, char *argv[])
{
    QCoreApplication a(argc, argv);

    QFile file("./template.txt");

    if(!file.open(QIODevice::ReadOnly))
    {
        qDebug()<<"can't find template.txt";
        return -1;
    }

    parseArgs(a.arguments());

    QVariantMap templateSetting = QJsonDocument::fromJson(file.readAll()).toVariant().toMap();

    QString root = templateSetting["root"].toString();
    auto tempsettings = templateSetting["options"].toMap();
    for(auto i : tempsettings.keys())
    {
        marks[i] = tempsettings[i].toString();
    }

    auto proj = readProject(root);

    QMap<QString,QString> markMap = getMarkMap();

    createProject(proj,outputDir,markMap);

    return a.exec();
}
